﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using ClientCommonWpf;
using Director.DomainModels;

namespace Director.ViewModels
{
	class PortfolioVM : AbstractViewModel
	{
		private readonly List<Position> _positions = new List<Position>();
		private readonly Dictionary<Security, decimal> _averagePrice = new Dictionary<Security, decimal>();
		private decimal _pnL;
		private decimal _просадкаПоОткрытымПозициям;
		private decimal _maxPnL;
		private decimal _minPnL;
		private int _buys=0, _sells=0;
		private decimal _maxSum;
		private readonly AbstractStrategyVM _abstractStrategyVM;

		public PortfolioVM(AbstractStrategyVM abstractStrategyVM)
		{
			_abstractStrategyVM = abstractStrategyVM;
		}

		public void Buy(Security security, decimal quantity, decimal price)
		{
			if (quantity <= 0) { throw new Exception("Количество не должно быть меньше или равно нулю"); }

			_pnL -= quantity * security.Lotsize * price * 0.03m.Percents();

			_buys++;

			var pos = Positions.FirstOrDefault(e => e.SecId == security.Id);
			
			if (pos == null)
			{
				Positions.Add(new Position { SecId = security.Id, Saldo = quantity });
				_averagePrice[security] = price;
			}
			else
			{
				if (pos.Saldo > 0)
				{
					_averagePrice[security] = (_averagePrice[security] * pos.Saldo + quantity * price) / (pos.Saldo + quantity);
					pos.Saldo += quantity;
				}
				else if (pos.Saldo < 0)
				{
					throw new NotImplementedException("С шортами не работаем");
				}
				else
				{
					throw new NotImplementedException("Нулевых позиций в портфеле быть не должно");
				}
			}

			_maxSum = Math.Max(Positions.Sum(e => e.Saldo * security.Lotsize * _averagePrice[security]), _maxSum);
		}

		public void Sell(Security security, decimal quantity, decimal price)
		{
			if(quantity<=0){throw new Exception("Количество не должно быть меньше или равно нулю");}

			_pnL -= quantity * security.Lotsize * price * 0.03m.Percents();
			_sells++;

			var pos = Positions.FirstOrDefault(e => e.SecId == security.Id);
			
			if (pos == null)
			{
				throw new InvalidEnumArgumentException();
			}

			if (pos.Saldo > 0)
			{
				var newQuantity = pos.Saldo - quantity;
				if (newQuantity<0)
				{
					throw new NotImplementedException("С переворотами не работаем");
				}

				var pnlAdd = quantity * security.Lotsize * (price - _averagePrice[security]);

				PnL += pnlAdd;

				if (newQuantity == 0)
				{
					Positions.Remove(pos);
				}
				else
				{
					pos.Saldo = newQuantity;
				}
			}
			else if (pos.Saldo < 0)
			{
				throw new NotImplementedException("С шортами не работаем");
			}
			else
			{
				throw new NotImplementedException("Нулевых позиций в портфеле быть не должно");
			}
		}

		public string Report
		{
			get
			{
				return String.Format(
@"PnL:			 {0:N}
ПросадкаОткрытых:	 {1:N}
Unrealized:		 {2:N}
ПросадкаСчета:		 {3:N}
K:			 {4:N1}
BS count:		{5}/{6}
Max in work		{7:N2}", PnL, ПросадкаПоОткрытымПозициям, UnrealizedPnL, ПросадкаПоСчету, ПросадкаПоСчету == 0 ? 0 : PnL / ПросадкаПоСчету, _buys, _sells, _maxSumInWork);
			}
		}

		public decimal PnL
		{
			get { return _pnL; }
			private set
			{
				_pnL = value;
				OnPropertyChanged(() => PnL);
				OnPropertyChanged(() => UnrealizedPnL);
				OnPropertyChanged(() => Report);
			}
		}

		public decimal UnrealizedPnL { get; private set; }

		public decimal Money { get; set; }

		public List<Position> Positions
		{
			get { return _positions; }
		}

		public decimal ПросадкаПоОткрытымПозициям
		{
			get { return _просадкаПоОткрытымПозициям; }
			set
			{
				_просадкаПоОткрытымПозициям = value;
				OnPropertyChanged(()=>ПросадкаПоОткрытымПозициям);
				OnPropertyChanged(() => Report);
			}
		}

		public decimal GetAveragePrice(Security security)
		{
			return _averagePrice[security];
		}

		private decimal _maxSumInWork = 0;

		public void CandlesUpdated()
		{
			
			var p = Positions.Where(e => e != null).ToArray();
			var poss = p.Where(e => e.Saldo != 0).ToDictionary(e => DataManager.Instance.Securities[e.SecId], e => e.Saldo).ToArray();
			var openSum = poss.Sum(e => e.Key.Lotsize * e.Value * _abstractStrategyVM.GetAveragePrice(e.Key));
			var closeSum = poss.Sum(e => e.Key.Lotsize * e.Value * _abstractStrategyVM.Last[e.Key].Close);
			UnrealizedPnL = _pnL + closeSum - openSum;

			_maxSumInWork = Math.Max(_maxSumInWork, openSum);

			OnPropertyChanged(()=>UnrealizedPnL);

			_maxPnL = Math.Max(UnrealizedPnL, _maxPnL);
			_minPnL = Math.Min(UnrealizedPnL, _minPnL);
			ПросадкаПоОткрытымПозициям = -Math.Min(-ПросадкаПоОткрытымПозициям, UnrealizedPnL - _pnL);
			ПросадкаПоСчету = Math.Max(ПросадкаПоСчету, _maxPnL - UnrealizedPnL);

			OnPropertyChanged(() => UnrealizedPnL);
			OnPropertyChanged(() => Report);
		}

		public decimal ПросадкаПоСчету { get; set; }
	}
}
